package com.itbaizhan.shopping_seckill_service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itbaizhan.shopping_common.pojo.CartGoods;
import com.itbaizhan.shopping_common.pojo.Orders;
import com.itbaizhan.shopping_common.pojo.SeckillGoods;
import com.itbaizhan.shopping_common.result.BusException;
import com.itbaizhan.shopping_common.result.CodeEnum;
import com.itbaizhan.shopping_common.service.SeckillService;
import com.itbaizhan.shopping_seckill_service.mapper.SeckillGoodsMapper;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.Scheduled;


import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@DubboService
public class SeckillServiceImpl implements SeckillService {
    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 核心代码：保证数据库与缓存数据的一致性(扩展：可以添加本地缓存Caffeine)
     */
    @Scheduled(cron = "0/30 * * * * *")
    public void refreshRedis() {
        // 更新mysql中的库存
        List<SeckillGoods> seckillGoodsOld = redisTemplate.boundHashOps("seckillGoods").values();
        for (SeckillGoods seckillGood : seckillGoodsOld) {
            // 根据商品id查询秒杀商品
            QueryWrapper<SeckillGoods> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("goodsId",seckillGood.getGoodsId());
            SeckillGoods seckillGoods1 = seckillGoodsMapper.selectOne(queryWrapper);
            // 更新库存
            seckillGoods1.setStockCount(seckillGood.getStockCount());
            seckillGoodsMapper.updateById(seckillGoods1);
        }

        // 向数据库查询符合条件的秒杀商品（开始时间<=当前<结束，库存>0）
        List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.findSeckillGoodsList();
        // 删除原来的数据
        redisTemplate.delete("seckillGoods");
        //同步到redis
        BoundHashOperations boundHashOps = redisTemplate.boundHashOps("seckillGoods");

        for (SeckillGoods seckillGoods : seckillGoodsList) {
            boundHashOps.put(seckillGoods.getGoodsId(),seckillGoods);
        }
    }

    @Override
    public Page<SeckillGoods> search(Integer page, Integer size) {
        // 查询所有秒杀商品
        List<SeckillGoods> seckillGoodsList = redisTemplate.boundHashOps("seckillGoods").values();
        // 截取当前商品列表
        // 开始索引
        int start = (page - 1) * size;
        // 结束索引
        int end = start + size > seckillGoodsList.size() ? seckillGoodsList.size() : start + size;
        List<SeckillGoods> seckillGoods = seckillGoodsList.subList(start, end);
        // 封装为 Page对象
        Page<SeckillGoods> seckillGoodsPage = new Page();
        seckillGoodsPage.setCurrent(page)
                .setRecords(seckillGoods)
                .setTotal(seckillGoodsList.size())
                .setSize(size);

        return seckillGoodsPage;
    }


    @Override
    public SeckillGoods findById(Long goodsId) {
        SeckillGoods seckillGoods =(SeckillGoods) redisTemplate.boundHashOps("seckillGoods").get(goodsId);
        return seckillGoods;
    }

    @Override
    public Orders createOrder(Orders orders) {
        // 手动生成订单编号
        orders.setId(IdWorker.getIdStr());
        // 设置订单状态为未支付
        orders.setStatus(1);
        // 订单创建时间
        orders.setCreateTime(new Date());
        // 订单过期时间
        orders.setExpire(new Date(new Date().getTime() + 1000*60*5));
        // 计算订单金额
        CartGoods cartGoods = orders.getCartGoods().get(0);
        Integer num = cartGoods.getNum();
        BigDecimal price = cartGoods.getPrice();
        BigDecimal sum = price.multiply(new BigDecimal(num));
        orders.setPayment(sum);

        // 扣减库存 (使用分布式锁解决商品超卖问题)
        // 1、查询秒杀商品
        Long goodId = cartGoods.getGoodId();
        String lockKey = "lock:seckill:" + goodId;
        try {
            boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
            if (!locked) {  //保证同一时刻只有一个线程操作该秒杀商品
                throw new BusException(CodeEnum.SECKILL_GOODS_STOCK_ERROR);
            }
            // 原有扣减逻辑...
            SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps("seckillGoods").get(goodId);
            if(seckillGoods==null){ //缓存没数据，直接查询数据库
                QueryWrapper<SeckillGoods> wrapper = new QueryWrapper<>();
                wrapper.eq("goodsId",goodId);
                seckillGoods = seckillGoodsMapper.selectOne(wrapper);
            }
            if(seckillGoods!=null && seckillGoods.getStockCount() >= num) {
                seckillGoods.setStockCount(seckillGoods.getStockCount() - num);
                redisTemplate.boundHashOps("seckillGoods").put(seckillGoods.getGoodsId(),seckillGoods);
            }else{
                throw new BusException(CodeEnum.SECKILL_GOODS_STOCK_ERROR);
            }
        } finally {
            redisTemplate.delete(lockKey);
        }

        // 保存订单数据到缓存
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.opsForValue().set(orders.getId(),orders,1, TimeUnit.MINUTES);
        redisTemplate.opsForValue().set(orders.getId()+"_copy",orders,2,TimeUnit.MINUTES);
        return orders;
    }

    @Override
    public Orders findOrder(String id) {
        return (Orders) redisTemplate.opsForValue().get(id);
    }

    @Override
    public Orders pay(String orderId) {
        // 查询订单
        Orders orders = (Orders)redisTemplate.opsForValue().get(orderId);
        orders.setStatus(2);
        orders.setPaymentTime(new Date());
        orders.setPaymentType(2);
        // 删除缓存中的订单数据
        redisTemplate.delete(orderId);
        redisTemplate.delete(orderId+"_copy");
        //返回订单
        return orders;
    }
}
